<?php
/**
 * Created by PhpStorm.
 * User: tian
 * Date: 2018/3/5
 * Time: 19:50
 * 用于进行二联体、三联体亲权鉴定以及同一性认定的计算
 * 调用方法： ForensicCalculate::instance()->setCaseType($caseType)->setGenotypeF($genotypeF)->setGenotypeC($genotypeC)->setFrequency($frequency)->setMutation($mutation)->calculate();
 */

namespace org\util\forensictool;


class ForensicCalculate
{
    protected static $instance; //object 对象实例
    protected $caseType='';        //案件类型,2l 3l ty
    protected $genotypeF=[];       //基因型数组
    protected $genotypeM=[];
    protected $genotypeC=[];
    protected $frequency=[];       //整个基因频率库数组
    protected $mutation=[];        //整个基因频率库的突变率数组

    /**
     * 实例化
     * @return ForensicCalculate
     */
    static public function instance()
    {
        if (is_null(self::$instance)) {
            self::$instance = new static();
        }
        return self::$instance;
    }

    /**
     * @param string $caseType
     * @return ForensicCalculate
     * @throws \Exception
     */
    public function setCaseType(string $caseType)
    {
        $map = ['2l', '3l', 'ty'];
        if (!in_array($caseType, $map)){
            throw new \Exception('错误的案件类型：' . var_dump($caseType));
        }else{
            $this->caseType=$caseType;
            return $this;
        }
    }

    /**
     * @param array $genotypeF
     * @return ForensicCalculate
     */
    public function setGenotypeF(array $genotypeF)
    {
        $this->genotypeF=$genotypeF;
        return $this;
    }

    /**
     * @param array $genotypeM
     * @return ForensicCalculate
     */
    public function setGenotypeM(array $genotypeM)
    {
        $this->genotypeM = $genotypeM;
        return $this;
    }

    /**
     * @param array $genotypeC
     * @return ForensicCalculate
     */
    public function setGenotypeC(array $genotypeC)
    {
        $this->genotypeC = $genotypeC;
        return $this;
    }

    /**
     * @param array $genotype0
     * @return ForensicCalculate
     */
    public function setGenotype0(array $genotype0)
    {
        $this->genotypeF=$genotype0;
        return $this;
    }

    /**
     * @param array $genotype1
     * @return ForensicCalculate
     */
    public function setGenotype1(array $genotype1)
    {
        $this->genotypeC=$genotype1;
        return $this;
    }

    /**
     * @param array $frequency
     * @return ForensicCalculate
     */
    public function setFrequency(array $frequency)
    {
        $this->frequency = $frequency;
        return $this;
    }

    /**
     * @param array $mutation
     * @return ForensicCalculate
     */
    public function setMutation(array $mutation)
    {
        $this->mutation = $mutation;
        return $this;
    }



    /**
     * @throws \Exception
     * @return array $result
     */
    public function calculate()
    {
        //错误处理
        if ($this->caseType != 'ty' && !is_array($this->mutation)) throw new \Exception('突变率不能为空');
        if ($this->caseType == '3l' && (!is_array($this->genotypeM) || empty($this->genotypeM))) throw new \Exception('母亲基因型不能为空');

        $markerInFrequency = array_keys($this->frequency);//频率库包含的基因座

        $markerF = array_keys($this->genotypeF);
        $markerC = array_keys($this->genotypeC);
        $markerM = array_keys($this->genotypeM);
        $marker_min = $markerF + $markerC + $markerM;//样品共有基因座
        $marker_max = array_merge($markerF, $markerC, $markerM);//样品包含的全部基因座

        $marker_cal = array_intersect($marker_min, $markerInFrequency);//参与计算的基因座

        $result = [];

        foreach ($marker_max as $marker) { //将基因频率整合进样品分型数组
            if(in_array($marker,$marker_cal)){
                $alleleF = $this->genotypeF[$marker];//单基因座分型
                $alleleC = $this->genotypeC[$marker];

                $frequencyThisMarker = $this->frequency[$marker];//当前基因座的频率表
                $minFrequency = min($frequencyThisMarker);//当前基因座的最小基因频率

                $frequencyC[0] = isset($frequencyThisMarker[$alleleC[0]]) ? $frequencyThisMarker[$alleleC[0]] : $minFrequency;//样品当前基因座的频率
                $frequencyC[1] = isset($frequencyThisMarker[$alleleC[1]]) ? $frequencyThisMarker[$alleleC[1]] : $minFrequency;

                $mutation=isset($this->mutation[$marker]) ? $this->mutation[$marker] : 0.002;

                switch($this->caseType){//按照案件类型进行计算
                    case '2l':
                        $result[$marker]=$this->calOneMarker2l($alleleF,$alleleC,$frequencyC,$mutation);
                        break;
                    case '3l':
                        $alleleM = $this->genotypeM[$marker];
                        $result[$marker]=$this->calOneMarker3l($alleleF,$alleleM,$alleleC,$frequencyC,$mutation);
                        break;
                    case 'ty':
                        $result[$marker]=$this->calOneMarkerTy($alleleF,$alleleC,$frequencyC);
                        break;
                }
            }else{
                $result[$marker]=[];//不参与计算的基因座
            }
        }
        return $result;
    }

    /**
     * @param array $alleleF
     * @param array $alleleC
     * @param array $frequency
     * @param array $mutation
     * @return array $PI
     */
    protected function calOneMarker2l($alleleF, $alleleC, $frequency, $mutation)
    {
        $PI = array(null, null, null);
        if ($alleleF[0] != $alleleC[0] && $alleleF[1] != $alleleC[0] && $alleleF[0] != $alleleC[1] && $alleleF[1] != $alleleC[1]) {
            $PI[2] = '不匹配';

            $num[0] = abs($alleleF[0] - $alleleC[0]);
            $num[1] = abs($alleleF[0] - $alleleC[1]);
            $num[2] = abs($alleleF[1] - $alleleC[0]);
            $num[3] = abs($alleleF[1] - $alleleC[1]);

            $frequency_cal[0] = $frequency[0];
            $frequency_cal[1] = $frequency[1];
            $frequency_cal[2] = $frequency[0];
            $frequency_cal[3] = $frequency[1];

            $alleleC_cal[0] = $alleleC[0];
            $alleleC_cal[1] = $alleleC[1];
            $alleleC_cal[2] = $alleleC[0];
            $alleleC_cal[3] = $alleleC[1];

            $num_min = 100;

            for ($i = 0; $i < 4; $i++) {

                if ($num[$i] < $num_min) {

                    if (is_int($num[$i])) {//判断相差重复数是否为整数
                        $num_min = $num[$i];
                    }

                    $frequency_fin = $frequency_cal[$i];
                    $alleleC_fin = $alleleC_cal[$i];
                }
            }
            if ($num_min == 100) {
                if (is_int(min($num))) {
                    $num_min = min($num);
                } else {
                    $num_min = intval(min($num)) + 1;
                }
            }

            if ($alleleC[0] == $alleleC[1]) {

                if (abs($alleleF[0] - $alleleC[0]) == abs($alleleF[1] - $alleleC[0])) {
                    $val = 2 * pow(10, abs($alleleF[0] - $alleleC[0]) - 1);
                    $PI[0] = $mutation / ($frequency[0] * $val);
                    $PI[1] = 'u/(' . $val . 'p{' . $alleleC[0] . '})';
                } else {
                    if (abs($alleleF[0] - $alleleC[0]) < abs($alleleF[1] - $alleleC[0])) {
                        $val = 4 * pow(10, abs($alleleF[0] - $alleleC[0]) - 1);
                        $PI[0] = $mutation / ($frequency[0] * $val);
                        $PI[1] = 'u/(' . $val . 'p{' . $alleleC[0] . '})';
                    } else {
                        $val = 4 * pow(10, abs($alleleF[1] - $alleleC[0]) - 1);
                        $PI[0] = $mutation / ($frequency[0] * $val);
                        $PI[1] = 'u/(' . $val . 'p{' . $alleleC[0] . '})';
                    }
                }
            } else {

                if ($alleleC[0] > $alleleC[1]) {
                    $alleleC_small = $alleleC[1];
                    $alleleC_large = $alleleC[0];
                    $frequency_small = $frequency[1];
                    $frequency_large = $frequency[0];
                } else {
                    $alleleC_small = $alleleC[0];
                    $alleleC_large = $alleleC[1];
                    $frequency_small = $frequency[0];
                    $frequency_large = $frequency[1];
                }
                if ($alleleF[0] > $alleleF[1]) {
                    $alleleF_small = $alleleF[1];
                    $alleleF_large = $alleleF[0];
                } else {
                    $alleleF_small = $alleleF[0];
                    $alleleF_large = $alleleF[1];
                }

                if ($alleleC_large - $alleleC_small == 2 && abs($alleleF_small - $alleleC_small) == abs($alleleF_large - $alleleC_large) && abs($alleleF_small - $alleleC_small) == 1) {

                    if ($alleleF_large == $alleleF_small) {
                        $PI[0] = $mutation * ($frequency_small + $frequency_large) / (4 * $frequency_small * $frequency_large);
                        $PI[1] = 'u(p{' . $alleleC_small . '}+q{' . $alleleC_large . '})/(4p{' . $alleleC_small . '}q{' . $alleleC_large . '})';
                    } elseif ($alleleF_large - $alleleF_small == 2) {
                        if ($alleleF_small - $alleleC_small < 0) {
                            $PI[0] = $mutation * (2 * $frequency_large + $frequency_small) / (8 * $frequency_small * $frequency_large);
                            $PI[1] = 'u(p{' . $alleleC_small . '}+2q{' . $alleleC_large . '})/(8p{' . $alleleC_small . '}q{' . $alleleC_large . '})';
                        } else {
                            $PI[0] = $mutation * ($frequency_large + 2 * $frequency_small) / (8 * $frequency_small * $frequency_large);
                            $PI[1] = 'u(2p{' . $alleleC_small . '}+q{' . $alleleC_large . '})/(8p{' . $alleleC_small . '}q{' . $alleleC_large . '})';
                        }

                    } elseif ($alleleF_large - $alleleF_small == 4) {
                        $PI[0] = $mutation * ($frequency_small + $frequency_large) / (8 * $frequency_small * $frequency_large);
                        $PI[1] = 'u(p{' . $alleleC_small . '}+q{' . $alleleC_large . '})/(8p{' . $alleleC_small . '}q{' . $alleleC_large . '})';
                    }

                } else {
                    if (($num[0] == 1 && ($num[1] == 1 || $num[3] == 1)) || ($num[2] == 1 && ($num[1] == 1 || $num[3] == 1))) {
                        $calcount = 2;
                    } else {
                        $calcount = 0;
                    }


                    if ($calcount == 2) {
                        $PI[0] = $mutation * ($frequency_small + $frequency_large) / (8 * $frequency_small * $frequency_large);
                        $PI[1] = 'u(p{' . $alleleC_small . '}+q{' . $alleleC_large . '})/(8p{' . $alleleC_small . '}q{' . $alleleC_large . '})';
                    } else {

                        if (abs($alleleF[0] - $alleleC_fin) == abs($alleleF[1] - $alleleC_fin)) {
                            $a = 0.5;
                        } else {
                            $a = 1;
                        }
                        $val = 8 * pow(10, $num_min - 1) * $a;
                        $PI[0] = $mutation / ($frequency_fin * $val);
                        $PI[1] = 'u/(' . $val . 'p{' . $alleleC_fin . '})';
                    }
                }
            }


        } elseif ($alleleC[0] == $alleleC[1]) {
            $PI[2] = '匹配';
            if ($alleleF[0] == $alleleF[1]) {
                $PI[0] = 1 / $frequency[0];
                $PI[1] = '1/p{' . $alleleC[0] . '}';
            } else {
                $PI[0] = 0.5 / $frequency[0];
                $PI[1] = '1/(2p{' . $alleleC[0] . '})';
            }


        } else {
            $PI[2] = '匹配';
            if ($alleleF[0] == $alleleF[1]) {
                if ($alleleF[0] == $alleleC[0]) {
                    $PI[0] = 0.5 / $frequency[0];
                    $PI[1] = '1/(2p{' . $alleleC[0] . '})';
                } elseif ($alleleF[0] == $alleleC[1]) {
                    $PI[0] = 0.5 / $frequency[1];
                    $PI[1] = '1/(2p{' . $alleleC[1] . '})';
                }

            } elseif ($alleleF[0] == $alleleC[0]) {
                if ($alleleF[1] == $alleleC[1]) {
                    $PI[0] = ($frequency[0] + $frequency[1]) / (4 * $frequency[0] * $frequency[1]);
                    $PI[1] = '(p{' . $alleleC[0] . '}+q{' . $alleleC[1] . '})/(4p{' . $alleleC[0] . '}q{' . $alleleC[1] . '})';
                } else {
                    $PI[0] = 0.25 / $frequency[0];
                    $PI[1] = '1/(4p{' . $alleleC[0] . '})';

                }

            } elseif ($alleleF[1] == $alleleC[0]) {
                if ($alleleF[0] == $alleleC[1]) {
                    $PI[0] = ($frequency[0] + $frequency[1]) / (4 * $frequency[0] * $frequency[1]);
                    $PI[1] = '(p{' . $alleleC[0] . '}+q{' . $alleleC[1] . '})/(4p{' . $alleleC[0] . '}q{' . $alleleC[1] . '})';
                } else {
                    $PI[0] = 0.25 / $frequency[0];
                    $PI[1] = '1/(4p{' . $alleleC[0] . '})';
                }
            } elseif ($alleleF[0] == $alleleC[1]) {
                $PI[0] = 0.25 / $frequency[1];
                $PI[1] = '1/(4p{' . $alleleC[1] . '})';
            } elseif ($alleleF[1] == $alleleC[1]) {
                $PI[0] = 0.25 / $frequency[1];
                $PI[1] = '1/(4p{' . $alleleC[1] . '})';
            } else {
                echo $alleleC[0] . ',' . $alleleC[1] . ',' . $alleleF[0] . ',' . $alleleF[1];
            }
        }
        return $PI;
    }


    /**
     * @param array $alleleF
     * @param array $alleleM
     * @param array $alleleC
     * @param array $frequency
     * @param array $mutation
     * @return array $PI
     */
    protected function calOneMarker3l($alleleF, $alleleM, $alleleC, $frequency, $mutation)
    {
        $PI = array();
        $needF = array();
        $frequency_needF = array();

        //获取推断父等位基因
        if ($alleleC[0] == $alleleC[1]) {

            $needF = array($alleleC[0]);
            $frequency_needF = array($frequency[0]);

        } else {

            if (in_array($alleleC[0], $alleleM) && in_array($alleleC[1], $alleleM)) {

                $needF = array($alleleC[0], $alleleC[1]);
                $frequency_needF = array($frequency[0], $frequency[1]);

            } elseif (in_array($alleleC[0], $alleleM)) {

                $needF = array($alleleC[1]);
                $frequency_needF = array($frequency[1]);

            } elseif (in_array($alleleC[1], $alleleM)) {

                $needF = array($alleleC[0]);
                $frequency_needF = array($frequency[0]);

            } else {
                var_dump($alleleC);
            }
        }

        //判断是否匹配
        $match = 0;

        for ($i = 0; $i < count($needF); $i++) {
            if (in_array($needF[$i], $alleleF)) {
                $match = 1;
            }
        }

        if ($match == 1) {//符合遗传规律

            $PI[2] = '匹配';
            if (count($needF) == 1) {

                if ($alleleF[0] == $alleleF[1]) {
                    $PI[0] = 1 / $frequency_needF[0];
                    $PI[1] = '1/p{' . $needF[0] . '}';
                } else {
                    $PI[0] = 0.5 / $frequency_needF[0];
                    $PI[1] = '1/2p{' . $needF[0] . '}';
                }

            } elseif (count($needF) == 2) {

                if (in_array($alleleF[0], $needF) && in_array($alleleF[1], $needF)) {
                    $PI[0] = 1 / ($frequency_needF[0] + $frequency_needF[0]);
                    $PI[1] = '1/(p{' . $needF[0] . '}+q{' . $needF[1] . '}';
                } else {
                    $PI[0] = 0.5 / ($frequency_needF[0] + $frequency_needF[0]);
                    $PI[1] = '1/[2(p{' . $needF[0] . '}+q{' . $needF[1] . '})]';
                }
            }


        } else {//不符合遗传规律情况

            $PI[2] = '不匹配';
            $step_min = 100;  //最小突变步数
            $num_min = 100; //最小差异数值
            $count_min = 0; //最小步数出现的次数
            $count_num_min = 0;//最小差异数值出现的次数

            for ($i = 0; $i < count($needF); $i++) {
                for ($j = 0; $j < 2; $j++) {
                    $num = abs($alleleF[$j] - $needF[$i]);
                    if (is_int($num) && $num < $step_min) {
                        $step_min = $num;
                        $count_min = 1;
                    } elseif (is_int($num) && $num == $step_min) {
                        $count_min++;
                    }

                    if ($num < $num_min) {
                        $num_min = $num;
                    } elseif ($num == $num_min) {
                        $count_num_min++;
                    }
                }
            }

            if ($step_min == 100) {
                $step_min = intval($num_min) + 1;
                $count_min = $count_num_min;
            }


            //公式参数
            $k = 4;
            $x = $step_min - 1;
            $y = $count_min;

            echo $step_min . ',' . $count_min . ',' . $needF[0] . ',' . $needF[1] . "<br />";

            if (count($needF) == 1) {

                $val_k = $k / $y * pow(10, $x);
                $PI[0] = $mutation / ($val_k * $frequency_needF[0]);
                $PI[1] = 'μ/' . $val_k . 'p{' . $needF[0] . '}';

            } elseif (count($needF) == 2) {

                $val_k = $k * pow(10, $x);
                $PI[0] = $y * $mutation / ($val_k * ($frequency_needF[0] + $frequency_needF[1]));

                if ($y == 1) {
                    $y = '';
                }

                $PI[1] = $y . 'μ/[' . $val_k . '(p{' . $needF[0] . '}+q{' . $needF[1] . '})]';

            }
        }

        return $PI;
    }

    /**
     * @param array $alleleA
     * @param array $alleleB
     * @param array $frequency
     * @return array $PI
     */
    protected function calOneMarkerTy($alleleA, $alleleB, $frequency)
    {

        //等位基因排序
        if ($alleleA[0] >= $alleleA[1]) {
            $alleleAL = $alleleA[0];
            $alleleAS = $alleleA[1];
        } else {
            $alleleAL = $alleleA[1];
            $alleleAS = $alleleA[0];
        }
        if ($alleleB[0] >= $alleleB[1]) {
            $alleleBL = $alleleB[0];
            $alleleBS = $alleleB[1];
        } else {
            $alleleBL = $alleleB[1];
            $alleleBS = $alleleB[0];
        }

        //判断是否匹配并计算
        if ($alleleAL == $alleleBL && $alleleAS == $alleleBS) {
            if ($alleleAL == $alleleAS) {
                $pi[0] = $frequency[0] * $frequency[1];
                $pi[1] = 'p{' . $alleleA[0] . '}^2';
                $pi[2] = '匹配';
            } else {
                $pi[0] = 2 * $frequency[0] * $frequency[1];
                $pi[1] = '2p{' . $alleleA[0] . '}q{' . $alleleA[1] . '}';
                $pi[2] = '匹配';
            }

        } else {
            $pi[0] = 0;
            $pi[1] = '';
            $pi[2] = '不匹配';
        }
        return $pi;

    }
}